#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <stdlib.h>

#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <liburing.h>

#include "tcp_log.h"
#include "tcp_conn.h"
#include "tcp_iouring.h"

TCP_CONNECTION_S g_astConn[1024];
uint32_t g_uiConnCount = 0;

static TCP_CONNECTION_S* __conn_Alloc()
{
    TCP_CONNECTION_S* pstConn = NULL;

    if (g_uiConnCount < 1024)
    {
        pstConn = &g_astConn[g_uiConnCount];
        pstConn->uiConnId = g_uiConnCount++;
        return pstConn;
    }

    return NULL;
}

static TCP_CONNECTION_S* __conn_Get(uint32_t uiConnId)
{
    if (uiConnId < 1024)
    {
        return &g_astConn[uiConnId];
    }

    return NULL;
}

TCP_CONNECTION_S* TCP_conn_CreateListener(const char* pcHostName, uint16_t usPort)
{
    int iEnable;
    int iErr;
    int iFd;
    struct sockaddr_in stLocalAddr;

    iErr = inet_pton(AF_INET, pcHostName, &stLocalAddr.sin_addr);
    if (iErr != 1)
    {
        TCP_ERROR("inet_pton error, %s", strerror(errno));
        return NULL;
    }

    iFd = socket(AF_INET, SOCK_STREAM, 0);
    if (iFd == -1)
    {
        TCP_ERROR("socket error, %s", strerror(errno));
        return NULL;
    }

    // 用于快速重启服务，当一个TCP套接字处于TIME_WAIT状态时，新启动的程序可以使用该选项来占用该地址和端口。
    // 通常，一个端口在释放后会等待两分钟才能再次被使用，但SO_REUSEADDR允许端口释放后立即被再次使用。
    iEnable = 1;
	iErr = setsockopt(iFd, SOL_SOCKET, SO_REUSEADDR, &iEnable, sizeof(int));
    if (iErr == -1)
    {
        TCP_ERROR("setsockopt error, %s", strerror(errno));
        close(iFd);
        return NULL;
    }

    // SO_REUSEPORT
    // 允许多个socket绑定在相同的地址上(identical socket address)，常用于负载分担

    stLocalAddr.sin_family = AF_INET;
    stLocalAddr.sin_port = htons(usPort);
    iErr = bind(iFd, (const struct sockaddr*)&stLocalAddr, sizeof(stLocalAddr));
    if (iErr == -1)
    {
        TCP_ERROR("bind error, %s", strerror(errno));
        close(iFd);
        return NULL;
    }

    iErr = listen(iFd, 8);
    if (iErr == -1)
    {
        TCP_ERROR("listen error, %s", strerror(errno));
        close(iFd);
        return NULL;
    }

    TCP_CONNECTION_S* pstConn = __conn_Alloc();
    if (pstConn == NULL)
    {
        TCP_ERROR("__conn_Alloc error");
        close(iFd);
        return NULL;
    }

    pstConn->iFd = iFd;
    pstConn->stLocalAddr = stLocalAddr;

    TCP_INFO("Listened on %s:%hu successfully, sockfd=%d", pcHostName, usPort, iFd);

    return pstConn;
}


static int __conn_HandleAccept(struct io_uring_cqe* pstCQE, TCP_CONNECTION_S* pstConn)
{
    if (pstCQE->res < 0)
    {
        TCP_ERROR("Failed to accept new client, %s", strerror(-pstCQE->res));
        return -1;
    }

    TCP_INFO("Accepted new client %s:%hu, sockfd=%d", inet_ntoa(pstConn->stPeerAddr.sin_addr), 
                ntohs(pstConn->stPeerAddr.sin_port), pstCQE->res);

    TCP_iouring_AddSQE(pstConn, TCP_CONN_ACCEPT);

    TCP_CONNECTION_S* pstSubConn = __conn_Alloc();
    if (pstSubConn == NULL)
    {
        TCP_ERROR("Connection overflow");
        close(pstCQE->res);
        return -1;
    }

    pstSubConn->stPeerAddr = pstConn->stPeerAddr;
    pstSubConn->iFd = pstCQE->res;
    pstSubConn->pcBuf = pstSubConn->acBuf;
    pstSubConn->ulBufLen = sizeof(pstSubConn->acBuf);
    TCP_iouring_AddSQE(pstSubConn, TCP_CONN_READ);

    return 0;
}

static int __conn_HandleRead(struct io_uring_cqe* pstCQE, TCP_CONNECTION_S* pstConn)
{
    if (pstCQE->res <= 0)
    {
        close(pstConn->iFd);
        TCP_ERROR("Connection close, peer=%s:%hu, read result=%d", 
                  inet_ntoa(pstConn->stPeerAddr.sin_addr), 
                  ntohs(pstConn->stPeerAddr.sin_port),
                  pstCQE->res);
        return -1;
    }

    TCP_INFO("Received message from %s:%hu, length=%d", inet_ntoa(pstConn->stPeerAddr.sin_addr), 
              ntohs(pstConn->stPeerAddr.sin_port), pstCQE->res);

    pstConn->ulDataLen = pstCQE->res;
    TCP_iouring_AddSQE(pstConn, TCP_CONN_WRITE);

    return 0;
}

static int __conn_HandleWrite(struct io_uring_cqe* pstCQE, TCP_CONNECTION_S* pstConn)
{
    if (pstCQE->res <= 0)
    {
        close(pstConn->iFd);
        TCP_ERROR("Connection close, peer=%s:%hu, write result=%d", 
                  inet_ntoa(pstConn->stPeerAddr.sin_addr), 
                  ntohs(pstConn->stPeerAddr.sin_port),
                  pstCQE->res);
        return -1;
    }

    TCP_INFO("Send message to %s:%hu, length=%d", inet_ntoa(pstConn->stPeerAddr.sin_addr), 
             ntohs(pstConn->stPeerAddr.sin_port), pstCQE->res);
    TCP_iouring_AddSQE(pstConn, TCP_CONN_READ);

    return 0;
}

int TCP_conn_Handle(struct io_uring_cqe* pstCQE)
{
    int iRet = -1;
    TCP_USER_DATA_S stUserData = 
    {
        .ui64Value = pstCQE->user_data,
    };

    TCP_DEBUG(1, "Handle event, event=%u, connid=%u", stUserData.uiConnId, stUserData.uiConnId);

    TCP_CONNECTION_S* pstConn = __conn_Get(stUserData.uiConnId);
    if (pstConn == NULL)
    {
        TCP_ERROR("Bad event, event=%u, connid=%u", stUserData.uiConnId, stUserData.uiConnId);
        return iRet;
    }

    switch (stUserData.enEvent)
    {
        case TCP_CONN_ACCEPT:
        {
            iRet = __conn_HandleAccept(pstCQE, pstConn);
            break;
        }
        case TCP_CONN_READ:
        {
            iRet = __conn_HandleRead(pstCQE, pstConn);
            break;
        }
        case TCP_CONN_WRITE:
        {
            iRet = __conn_HandleWrite(pstCQE, pstConn);
            break;
        }
        default:
        {
            TCP_ERROR("Bad event, %u\n", stUserData.enEvent);
            break;
        }
    }

    return iRet;
}

